package p05;

import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import p05.mapper.Mapper1;

import java.io.IOException;

/**
 * Mapper接口的解析 spring如何解析@Mapper的
 * 已知:MapperFactoryBean放入容器后，会根据Mapper接口创建Mapper对象(动态代理)
 * Mybatis有一个自动装配类，会把一个BeanFactory后处理器放入容器，该后处理器就负责解析@Mapper注解，并创建对应的MapperFactoryBean放入容器
 * 所以：这个解析MapperFactoryBean的后处理器原理根ComponentScan注解差不多，也是扫描包，扫到Mapper接口就创建一个MapperFactoryBean放入容器
 * 这里我们模拟了Mybatis的BeanFactory后处理器，该后处理器没有实现BeanFactoryPostProcessor，而是实现了BeanDefinitionRegistryPostProcessor
 * BeanDefinitionRegistryPostProcessor也是个后处理器，BeanFactoryPostProcessor的子类，加入容器后，初始化时会自动执行其两个方法
 * 本节知识点：
 * Mapper接口被Spring管理的本质：实际是被作为MapperFactoryBean(封装了Mapper接口，能得到一些信息比如sql)注册到容器中
 *
 * @author Chen weishuai
 * @since 2025/7/29
 */
public class ResolveMapper {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean(ConfigurationClassPostProcessor.class);

        context.registerBean(Config.class);
        context.registerBean(MyResolveMapper.class);
        context.refresh();

        Mapper1 bean = context.getBean(Mapper1.class);
        System.out.println(bean.count());
    }

    static class MyResolveMapper implements BeanDefinitionRegistryPostProcessor {

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            AnnotationBeanNameGenerator nameGenerator = new AnnotationBeanNameGenerator();
            PathMatchingResourcePatternResolver pathResolver = new PathMatchingResourcePatternResolver();
            CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();

            try {
                Resource[] resources = pathResolver.getResources("classpath:p05/mapper/**/*.class");
                for (Resource resource : resources) {
                    MetadataReader metadataReader = readerFactory.getMetadataReader(resource);
                    // 是否接口类型
                    if (metadataReader.getClassMetadata().isInterface()) {
                        //每扫描到一个Mapper接口，就往容器中放一个MapperFactoryBean(MapperFactoryBean的作用前面已经讲了，
                        // 就是根据接口使用动态代理生成实现类)
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(MapperFactoryBean.class)
                                .addConstructorArgValue(metadataReader.getClassMetadata().getClassName())
                                .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                                .getBeanDefinition();
                        AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(
                                metadataReader.getClassMetadata().getClassName()
                        ).getBeanDefinition();
                        String mapperFactoryBeanName = nameGenerator.generateBeanName(bd, registry);
                        // 注册Bean
                        registry.registerBeanDefinition(mapperFactoryBeanName, beanDefinition);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        }
    }
}
